home *** CD-ROM | disk | FTP | other *** search
/ Hyper Stacks 1994 May / Hyper Stacks (Pacific HiTech)(1994)[Mac].iso / Organization / TEX ƒ / zbrowser 0.5.c < prev   
Text File  |  1988-09-04  |  38KB  |  1,351 lines

  1. /* New "TEX" master XFCN "zbrowser(...)" - by ^z - 1988 Aug 22-Sep 4...
  2.  *
  3.  * copyright 1988 by Mark "^z" Zimmermann - all rights reserved.
  4.  * (Symantec/Think Technologies may also have copyrights on portions.)
  5.  * All standard legalistic disclaimers are included herewith by reference.
  6.  * If, for example, your use of this software causes a thermonuclear
  7.  * war and the destruction of civilization (as we know it), I am
  8.  * not responsible.  Use the program at your own risk!!!  I have
  9.  * never lost any data with it -- but there are no guarantees in life.
  10.  *
  11.  * This XFCN was developed using Think's Lightspeed C.  It is available
  12.  * for nonexclusive licensing at a cost of 2% of retail price,
  13.  * per distributed copy of any for-sale software
  14.  * that uses it (as of Sept. 1988).  My HyperCard stack using this XFCN,
  15.  * "TEX", is available via CompuServe, Arpanet, and other media.
  16.  *
  17.  ***********************************************************************
  18.  *   Individual users of TEX should send a $10 license fee to me at    *
  19.  *   the below address. The corporate license fee is $40 per copy in   *
  20.  *   simultaneous use.                                                 *
  21.  ***********************************************************************
  22.  *
  23.  * In exchange for your license fee, you get:
  24.  *    - information about extensions and enhancements to this program
  25.  *        as they are come out;
  26.  *    - free support and advice on its use;
  27.  *    - copies of new versions for the cost of media and reproduction;
  28.  *    - a nice warm feeling knowing that you are supporting further
  29.  *        research into massive free-text dataspace tool-building.
  30.  *
  31.  * Be a part of the adventure!  If you can't afford cash, take the time
  32.  * to write me a nice letter about how you're using TEX, or send a
  33.  * disk with your ideas/suggestions/modifications to the stack.
  34.  * 
  35.  * All monies received are used to enhance this software and to pay for
  36.  * creation and distribution expenses.  Comparable "commercial" free-text
  37.  * database products cost hundreds or thousands of dollars.  You have
  38.  * something precious here -- take advantage of it, please!  Build upon
  39.  * my work, extend it, and share your results with the community of
  40.  * researchers.
  41.  *
  42.  * I am very proud of this software.  I have chosen to distribute it
  43.  * at nominal cost, so that it can be more widely used and can help
  44.  * more people.  Please join me!
  45.  *
  46.  * For further information, and for license fee payments, write:
  47.  *    Mark ^Zimmermann
  48.  *    9511 Gwyndale Drive
  49.  *    Silver Spring, MD  20910
  50.  *    USA
  51.  *
  52.  * Be sure to enclose a STAMPED, SELF-ADDRESSED ENVELOPE if you want
  53.  * to receive a timely reply!
  54.  *
  55.  * Electronic addresses:
  56.  *     science@nems.arpa
  57.  *     [75066,2044] CompuServe
  58.  *     tel. 301-565-2166
  59.  */
  60.  
  61.  
  62. /* see the end of this file for detailed information on the various
  63.  * TEX functions and their calling conventions
  64.  */
  65.  
  66. /* ------------------------header files to include-------------- */
  67.  
  68. #include <MacTypes.h>
  69. #include <FileMgr.h>
  70. #include <HyperXCmd.h>
  71.  
  72.  
  73. /* ---------------declarations and definitions------------------- */
  74.  
  75.  
  76. /* KEY_LENGTH is the number of letters we have in each index record;
  77.  * 28 is the value chosen for the past year as optimal...don't
  78.  * change it without good reason!
  79.  */
  80. #define KEY_LENGTH    28
  81.  
  82. /* SUBSPACE_QUANTUM is the distance in bytes that defines the proximity
  83.  * neighborhood of a word in the index ... it is used when defining a
  84.  * subset for pseudo-proximity searching.... SUBSPACE_QUANTUM * 8 is the
  85.  * effective compression factor for squeezing the text file down into
  86.  * an array of one-bit flags showing which regions of the dataspace are
  87.  * in the current working subspace.
  88.  *
  89.  * Thus, SUBSPACE_QUANTUM = 32, a nice choice, defines a chunkiness of 32
  90.  * characters in making comparisons for proximity determination purposes,
  91.  * and results in a compression factor of 256.  Thus, in a typical 1 MB Mac
  92.  * with ~100kB free running HyperCard, that should allow subspace browsing
  93.  * of dataspaces up to ~25 MB....
  94.  */
  95. #define SUBSPACE_QUANTUM  32
  96.  
  97. /* structure of the records in the index key file:
  98.  *    a fixed-length character string, padded out with blanks and
  99.  *        containing the unique alphanumeric 'words' in the document
  100.  *        file, changed to all-capital letters;
  101.  *    a cumulative count of how many total occurrences of words, including
  102.  *        the current one, have appeared up to this point in the sorted
  103.  *        index.
  104.  */
  105. typedef struct
  106.   {
  107.     char kkey[KEY_LENGTH];
  108.     long ccount;
  109.   }  KEY_RECORD;
  110.  
  111. /* some symbolic values... */
  112. #define TRUE  1
  113. #define FALSE 0
  114. #define NULL  0
  115.  
  116. /* define these to allow use of global variables in my XFCNs; a
  117.  * Lightspeed C necessity, apparently...
  118.  */
  119.  
  120. #define SetUpA4()        asm  {    move.l a4,-(sp)        \
  121.                                 move.l a0,a4    }
  122.  
  123. #define RestoreA4()        asm  {    move.l (sp)+,a4    }
  124.  
  125.  
  126. /* ---------------prototypes------------------- */
  127.  
  128. pascal void main (XCmdBlockPtr paramPtr);
  129. void doContext (XCmdBlockPtr paramPtr);
  130. void doEmptySubspace (XCmdBlockPtr paramPtr);
  131. void doFillSubspace (XCmdBlockPtr paramPtr);
  132. void doIndex (XCmdBlockPtr paramPtr);
  133. void doLocate (XCmdBlockPtr paramPtr);
  134. void doNewSubspace (XCmdBlockPtr paramPtr);
  135. void doReleaseSubspace (XCmdBlockPtr paramPtr);
  136. void doSetSubspaceBits (XCmdBlockPtr paramPtr);
  137. void doText (XCmdBlockPtr paramPtr);
  138. void returnErrorMsg (XCmdBlockPtr paramPtr, char *msg);
  139. void getKeyRecord (KEY_RECORD *keyRecp, long keyRecNum, int keyFileRefNum);
  140. long getTextPtr (long instanceNum, int ptrFileRefNum);
  141. void getContextLine (long bytes, long textPtr, int refNum, char *ansp);
  142. void buildIndexAnswer (char *ansp, int indexCountWidth, int indexKeyWidth,
  143.   long count, char key[]);
  144. void buildSubIndexAnswer (char *ansp, int indexCountWidth,
  145.   int indexKeyWidth, long maxIndexSampleCount, long prevCcount,
  146.   long thisCcount, char key[], Handle subspaceHandle, int ptrFileRefNum);
  147. int inSubspace (long textPtr, Handle subspaceHandle);
  148. void setSSBit (long textPtr, int setOrClear, Handle subspaceHandle);
  149. char *strcpy (char *s1, char *s2);
  150. int strlen (char *s);
  151. int zstrcmp (unsigned char *s1, unsigned char *s2);
  152. long atol (char *s);
  153. void ltoaR (char *ansp, long n, int maxDigits);
  154.  
  155.  
  156. /* ------------------------main program-------------- */
  157.  
  158.  
  159. /* main routine, to dispatch control to a function
  160.  * defined by the first letter of the first argument of the XFCN
  161.  */
  162.  
  163. pascal void main (paramPtr)
  164.   XCmdBlockPtr paramPtr;
  165.   {
  166.     SetUpA4();
  167.     switch (**(paramPtr->params[0]))
  168.       {
  169.           case 'C':
  170.               doContext (paramPtr);
  171.               break;
  172.           case 'E':
  173.               doEmptySubspace (paramPtr);
  174.               break;
  175.           case 'F':
  176.               doFillSubspace (paramPtr);
  177.               break;
  178.           case 'I':
  179.               doIndex (paramPtr);
  180.               break;
  181.           case 'L':
  182.               doLocate (paramPtr);
  183.               break;
  184.           case 'N':
  185.               doNewSubspace (paramPtr);
  186.               break;
  187.           case 'R':
  188.               doReleaseSubspace (paramPtr);
  189.               break;
  190.           case 'S':
  191.               doSetSubspaceBits (paramPtr);
  192.               break;
  193.           case 'T':
  194.               doText (paramPtr);
  195.               break;
  196.           default:
  197.               returnErrorMsg (paramPtr,
  198.                 "{Sorry, unrecognized command in TEX zbrowser XFCN call!}");
  199.               break;
  200.       }
  201.     RestoreA4();
  202.     return;
  203.   }
  204.  
  205.  
  206. /* ---------------------major functional units-------------- */
  207.  
  208.  
  209. /* function to create the context display...
  210.  *
  211.  *    ("CONTEXT", instanceNum, contextLines, targetContextLine,
  212.  *      contextLineLength, contextWordOffset, maxContextLinesSkipped,
  213.  *      ptrFileRefNum, textFileRefNum, subspaceHandle)
  214.  *    --    returns with contextLines of display followed by contextLines
  215.  *        of instanceNum-textPtr pairs, with context instance instanceNum
  216.  *        on line targetContextLine...
  217.  */
  218.  
  219. void doContext (paramPtr)
  220.   XCmdBlockPtr paramPtr;
  221.   {
  222.     int contextLines, targetContextLine, contextLineLength,
  223.       contextWordOffset, ptrFileRefNum, textFileRefNum, line;
  224.     long instanceNum, maxContextLinesSkipped, textPtr, j, *tempNum;
  225.     Handle subspaceHandle, answer, tempStor;
  226.     char *ansp;
  227.  
  228.     if (paramPtr->paramCount != 10)
  229.       {
  230.           returnErrorMsg (paramPtr,
  231.             "{Sorry, wrong number of parameters in XFCN CONTEXT call!}");
  232.           return;
  233.       }
  234.  
  235.     instanceNum = atol (*(paramPtr->params[1]));
  236.     contextLines = atol (*(paramPtr->params[2]));
  237.     targetContextLine = atol (*(paramPtr->params[3]));
  238.     contextLineLength = atol (*(paramPtr->params[4]));
  239.     contextWordOffset = atol (*(paramPtr->params[5]));
  240.     maxContextLinesSkipped = atol (*(paramPtr->params[6]));
  241.     ptrFileRefNum = atol (*(paramPtr->params[7]));
  242.     textFileRefNum = atol (*(paramPtr->params[8]));
  243.     subspaceHandle = (Handle) atol (*(paramPtr->params[9]));
  244.  
  245.     if (instanceNum < 0 || contextLines < 1 || targetContextLine < 1 ||
  246.             targetContextLine > contextLines ||
  247.             contextLineLength < KEY_LENGTH + contextWordOffset ||
  248.             contextWordOffset < 0 ||  maxContextLinesSkipped < 1 ||
  249.             ptrFileRefNum == NULL || textFileRefNum == NULL)
  250.       {
  251.           returnErrorMsg (paramPtr,
  252.             "{Sorry, bad parameter in XFCN CONTEXT call!}");
  253.           return;
  254.       }
  255.  
  256.     if ((answer = NewHandle (contextLines * (contextLineLength + 26) + 1))
  257.             == NULL)
  258.       {
  259.           returnErrorMsg (paramPtr,
  260.             "{Sorry, out of memory error in XFCN CONTEXT call!}");
  261.           return;
  262.       }
  263.  
  264.     /* tempStor is used to store values for instanceNums and textPtrs */
  265.     if ((tempStor = NewHandle (contextLines * 2 * sizeof(long))) == NULL)
  266.       {
  267.           DisposHandle (answer);
  268.           returnErrorMsg (paramPtr,
  269.             "{Sorry, secondary out-of-memory error in XFCN CONTEXT call!}");
  270.           return;
  271.       }
  272.     
  273.     /* back up to the right starting instanceNum */
  274.     for (line = targetContextLine; line > 1; --line)
  275.       {
  276.           for (j = 0; j < maxContextLinesSkipped; ++j)
  277.             {
  278.             textPtr = getTextPtr (--instanceNum, ptrFileRefNum);
  279.             if (subspaceHandle == NULL || textPtr < 0 ||
  280.                     inSubspace (textPtr, subspaceHandle))
  281.                 break;
  282.           }
  283.       }
  284.     
  285.     HLock (answer);
  286.     HLock (tempStor);
  287.     ansp = *answer;
  288.     tempNum = (long *) *tempStor;
  289.  
  290.     /* generate the lines of the context display, saving numbers */
  291.     for (line = 0; line < contextLines; ++line)
  292.       {
  293.           for (j = 0; j < maxContextLinesSkipped; ++j, ++instanceNum)
  294.             {
  295.             textPtr = getTextPtr (instanceNum, ptrFileRefNum);
  296.             if (textPtr < 0)
  297.                 break;
  298.             if (subspaceHandle == NULL ||
  299.                     inSubspace (textPtr, subspaceHandle))
  300.               {
  301.                 getContextLine (contextLineLength,
  302.                     textPtr - contextWordOffset, textFileRefNum, ansp);
  303.                 ansp += contextLineLength;
  304.                 break;
  305.               }
  306.           }
  307.         if (j == maxContextLinesSkipped)
  308.           {
  309.               textPtr = -1;
  310.               --instanceNum;
  311.               for (j = 0; j < contextLineLength; ++j)
  312.                   *ansp++ = '.';
  313.           }
  314.         tempNum[line] = instanceNum++;
  315.         tempNum[line + contextLines] = textPtr;
  316.         *ansp++ = '\r';
  317.       }
  318.  
  319.     for (line = 0; line < contextLines; ++line)
  320.       {
  321.         ltoaR (ansp, tempNum[line], 12);
  322.         ansp += 12;
  323.         ltoaR (ansp, tempNum[line + contextLines], 12);
  324.         ansp += 12;
  325.         *ansp++ = '\r';
  326.       }
  327.  
  328.     *ansp = '\0';
  329.     HUnlock (answer);
  330.     paramPtr->returnValue = answer;
  331.     HUnlock (tempStor);
  332.     DisposHandle (tempStor);
  333.     return;
  334.   }
  335.  
  336.  
  337. /* function to empty out a subspace so that no words are in the valid
  338.  * region:
  339.  *
  340.  *    ("EMPTYSUBSPACE", subspaceHandle)
  341.  *    --    returns quietly with nothing if it successfully sets all bits
  342.  *        in the subspace flag array to zero; beeps and gives an error msg
  343.  *        if it fails somehow...
  344.  */
  345.  
  346. void doEmptySubspace (paramPtr)
  347.   XCmdBlockPtr paramPtr;
  348.   {
  349.     long subspaceSize;
  350.     Handle subspaceHandle;
  351.     register char *cp, *endOfSubspace;
  352.  
  353.     if (paramPtr->paramCount != 2)
  354.       {
  355.           returnErrorMsg (paramPtr,
  356.             "{Sorry, wrong # of parameters in XFCN EMPTYSUBSPACE call!}");
  357.           return;
  358.       }
  359.      
  360.     subspaceHandle = (Handle) atol (*(paramPtr->params[1]));
  361.     
  362.     if (subspaceHandle == NULL)
  363.       {
  364.           returnErrorMsg (paramPtr,
  365.             "{Sorry, NULL subspaceHandle in XFCN EMPTYSUBSPACE call!}");
  366.           return;
  367.       }
  368.     
  369.     subspaceSize = GetHandleSize (subspaceHandle);
  370.     endOfSubspace = *subspaceHandle + subspaceSize;
  371.     for (cp = *subspaceHandle; cp < endOfSubspace; ++cp)
  372.         *cp = 0x00;
  373.         
  374.     return;
  375.   }
  376.  
  377.  
  378. /* function to fill a subspace so that the entire dataspace is in the
  379.  *    valid region:
  380.  *
  381.  *    ("FILLSUBSPACE", subspaceHandle)
  382.  *    --    returns quietly with nothing if it successfully sets all bits
  383.  *        in the subspace flag array to one; beeps and gives an error msg
  384.  *        if failure...
  385.  */
  386.  
  387. void doFillSubspace (paramPtr)
  388.   XCmdBlockPtr paramPtr;
  389.   {
  390.     long subspaceSize;
  391.     Handle subspaceHandle;
  392.     register char *cp, *endOfSubspace;
  393.  
  394.     if (paramPtr->paramCount != 2)
  395.       {
  396.           returnErrorMsg (paramPtr,
  397.             "{Sorry, wrong # of parameters in XFCN FILLSUBSPACE call!}");
  398.           return;
  399.       }
  400.      
  401.     subspaceHandle = (Handle) atol (*(paramPtr->params[1]));
  402.     
  403.     if (subspaceHandle == NULL)
  404.       {
  405.           returnErrorMsg (paramPtr,
  406.             "{Sorry, NULL subspaceHandle in XFCN FILLSUBSPACE call!}");
  407.           return;
  408.       }
  409.     
  410.     subspaceSize = GetHandleSize (subspaceHandle);
  411.     endOfSubspace = *subspaceHandle + subspaceSize;
  412.     for (cp = *subspaceHandle; cp < endOfSubspace; ++cp)
  413.         *cp = 0xFF;
  414.         
  415.     return;
  416.   }
  417.  
  418.  
  419. /* function to produce the index window display and associated info
  420.  *
  421.  *    ("INDEX", wordNum, indexLines, maxIndexSampleCount, indexCountWidth,
  422.  *      indexKeyWidth, keyFileRefNum, ptrFileRefNum, subspaceHandle)
  423.  *    --    returns with indexLines of index window display, followed by
  424.  *        indexLines of instanceNums.  The index lines are:
  425.  *        indexCountWidth columns of occurrence count info (right-justified),
  426.  *        a blank column, and indexKeyWidth columns of keyWord (in all
  427.  *        caps, left-justified).  Demand that indexCountWidth be at least
  428.  *        5, to allow for subindex count display, and that indexKeyWidth
  429.  *        be in the range 1 through KEY_LENGTH = 28 ...
  430.  */
  431.  
  432. void doIndex (paramPtr)
  433.   XCmdBlockPtr paramPtr;
  434.   {
  435.     KEY_RECORD thisRec, prevRec;
  436.     register int i;
  437.     int indexLines, keyFileRefNum, indexCountWidth, ptrFileRefNum,
  438.       keyRecsFound, indexKeyWidth;
  439.     long wordNum, maxIndexSampleCount;
  440.     Handle subspaceHandle, answer;
  441.     char *ansp;
  442.     
  443.     if (paramPtr->paramCount != 9)
  444.       {
  445.           returnErrorMsg (paramPtr,
  446.             "{Sorry, wrong number of parameters in XFCN INDEX call!}");
  447.           return;
  448.       }
  449.      
  450.     wordNum = atol (*(paramPtr->params[1]));
  451.     indexLines = atol (*(paramPtr->params[2]));
  452.     maxIndexSampleCount = atol (*(paramPtr->params[3]));
  453.     indexCountWidth = atol (*(paramPtr->params[4]));
  454.     indexKeyWidth = atol (*(paramPtr->params[5]));
  455.     keyFileRefNum = atol (*(paramPtr->params[6]));
  456.     ptrFileRefNum = atol (*(paramPtr->params[7]));
  457.     subspaceHandle = (Handle) atol (*(paramPtr->params[8]));
  458.     
  459.     if (wordNum < 0 || indexLines < 1 || maxIndexSampleCount < 1 ||
  460.             indexCountWidth < 5 || indexKeyWidth < 1 ||
  461.              indexKeyWidth > KEY_LENGTH || keyFileRefNum == 0 ||
  462.              ptrFileRefNum == 0)
  463.       {
  464.           returnErrorMsg (paramPtr,
  465.             "{Sorry, bad parameter in XFCN INDEX call!}");
  466.           return;
  467.       }
  468.  
  469.     
  470.     if ((answer = NewHandle (indexLines *
  471.                     (indexCountWidth + indexKeyWidth + 15) + 1)) == NULL)
  472.       {
  473.           returnErrorMsg (paramPtr,
  474.             "{Sorry, out of memory error in XFCN INDEX call!}");
  475.           return;
  476.       }
  477.  
  478.     HLock (answer);
  479.     ansp = *answer;
  480.  
  481.     getKeyRecord (&prevRec, wordNum - 1, keyFileRefNum);
  482.     
  483.     for (i = 0; i < indexLines; ++i)
  484.       {
  485.         getKeyRecord (&thisRec, wordNum + i, keyFileRefNum);
  486.         if (thisRec.ccount == 0)
  487.             break;
  488.             
  489.         if (subspaceHandle == NULL)
  490.             buildIndexAnswer (ansp, indexCountWidth, indexKeyWidth,
  491.                         thisRec.ccount - prevRec.ccount, thisRec.kkey);
  492.         else
  493.             buildSubIndexAnswer (ansp, indexCountWidth, indexKeyWidth,
  494.                 maxIndexSampleCount, prevRec.ccount, thisRec.ccount,
  495.                 thisRec.kkey, subspaceHandle, ptrFileRefNum);
  496.                         
  497.         ansp += indexCountWidth + indexKeyWidth + 2;
  498.         prevRec.ccount = thisRec.ccount;
  499.       }
  500.     
  501.     keyRecsFound = i;
  502.     for (i = keyRecsFound; i < indexLines; ++i)
  503.         *ansp++ = '\r';
  504.  
  505.     for (i = 0; i < keyRecsFound; ++i)
  506.       {
  507.         getKeyRecord (&thisRec, wordNum + i - 1, keyFileRefNum);
  508.         ltoaR (ansp, thisRec.ccount, 12);
  509.         ansp += 12;
  510.         *ansp++ = '\r';
  511.       }
  512.  
  513.     for (i = keyRecsFound; i < indexLines; ++i)
  514.         *ansp++ = '\r';
  515.     
  516.     *ansp = '\0';
  517.     HUnlock (answer);
  518.     paramPtr->returnValue = answer;
  519.     return;
  520.   }
  521.  
  522.  
  523. /* function to find a chosen string in the index key file (just do a
  524.  * binary search to locate it):
  525.  *
  526.  *    ("LOCATE", targetString, keyFileRefNum)
  527.  *    --    returns wordNum for the targetString if it is found in the
  528.  *        key file; otherwise returns wordNum for the word alphabetically
  529.  *        preceding targetString followed by "{targetString not found!}"
  530.  *        on the second line of the answer...
  531.  *
  532.  */
  533.  
  534. void doLocate (paramPtr)
  535.   XCmdBlockPtr paramPtr;
  536.   {
  537.       register int i, c;
  538.       int keyFileRefNum, diff;
  539.       char *cp;
  540.       register long mid;
  541.     long low, high, keyFileSize;
  542.     KEY_RECORD thisRec, targetRec;
  543.     Handle answer;
  544.     
  545.     if (paramPtr->paramCount != 3)
  546.       {
  547.           returnErrorMsg (paramPtr,
  548.             "{Sorry, wrong number of parameters in XFCN LOCATE call!}");
  549.           return;
  550.       }
  551.  
  552.     keyFileRefNum = atol (*(paramPtr->params[2]));
  553.  
  554.     if (keyFileRefNum == NULL)
  555.       {
  556.           returnErrorMsg (paramPtr,
  557.             "{Sorry, NULL keyFileRefNum error in XFCN LOCATE call!}");
  558.           return;
  559.       }
  560.  
  561.     cp = *(paramPtr->params[1]);
  562.     for (i = 0; i < KEY_LENGTH; ++i)
  563.       {
  564.           c = *cp;
  565.           if (c == '\0')
  566.             {
  567.               targetRec.kkey[i] = ' ';
  568.               continue;
  569.             }
  570.           if (c >= 'a' && c <= 'z')
  571.               c = c - 'a' + 'A';
  572.           targetRec.kkey[i] = c;
  573.           ++cp;
  574.       }
  575.       
  576.     low = 0;
  577.     GetEOF (keyFileRefNum, &keyFileSize);
  578.     high = keyFileSize / sizeof (KEY_RECORD) - 1;
  579.  
  580.     while (low <= high)
  581.       {
  582.         mid = (low + high) / 2;
  583.         getKeyRecord (&thisRec, mid, keyFileRefNum);
  584.         if (thisRec.ccount == 0)
  585.           {
  586.               returnErrorMsg (paramPtr,
  587.                 "{Sorry, possible file I/O error in XFCN LOCATE call!}");
  588.               return;
  589.           }
  590.         diff = zstrcmp ((unsigned char *)targetRec.kkey,
  591.                         (unsigned char *)thisRec.kkey);
  592.         if (diff < 0)
  593.             high = mid - 1;
  594.         else if (diff > 0)
  595.             low = mid + 1;
  596.         else
  597.           break;
  598.       }
  599.  
  600.     if (diff < 0)
  601.         --mid;
  602.     if (mid < 0)
  603.         mid = 0;
  604.     
  605.     if ((answer = NewHandle (64)) == NULL)
  606.       {
  607.           returnErrorMsg (paramPtr,
  608.             "{Sorry, out of memory error in XFCN LOCATE call!}");
  609.           return;
  610.       }
  611.     ltoaR (*answer, mid, 12);
  612.     *(*answer + 12) = '\0';
  613.     if (diff != 0)
  614.         strcpy (*answer + 12, "\r{target string not found!}");
  615.     paramPtr->returnValue = answer;
  616.     return;
  617.   }
  618.  
  619.  
  620. /* function to create a new subspace:
  621.  *
  622.  *    ("NEWSUBSPACE", textFileRefNum)
  623.  *    --    returns subspaceHandle for a new subspace that it creates, big
  624.  *        enough to do subspace browsing -- but does NOT initialize that
  625.  *        subspace or check to see whether another subspace already
  626.  *        exists.  Beeps and gives error msg if it fails...
  627.  */
  628.  
  629. void doNewSubspace (paramPtr)
  630.   XCmdBlockPtr paramPtr;
  631.   {
  632.     int textFileRefNum;
  633.     long textFileSize, subspaceSize;
  634.     Handle subspaceHandle, answer;
  635.     
  636.     if (paramPtr->paramCount != 2)
  637.       {
  638.           returnErrorMsg (paramPtr,
  639.             "{Sorry, wrong # of parameters in XFCN NEWSUBSPACE call!}");
  640.           return;
  641.       }
  642.  
  643.     textFileRefNum = atol (*(paramPtr->params[1]));
  644.     if (textFileRefNum == NULL ||
  645.         GetEOF (textFileRefNum, &textFileSize) != noErr)
  646.       {
  647.           returnErrorMsg (paramPtr,
  648.             "{Sorry, file error in XFCN NEWSUBSPACE call!}");
  649.           return;
  650.       }
  651.  
  652.     subspaceSize = 1 + textFileSize / (SUBSPACE_QUANTUM * 8);
  653.     
  654.     if ((subspaceHandle = NewHandle (subspaceSize)) == NULL)
  655.       {
  656.           returnErrorMsg (paramPtr,
  657.             "{Sorry, not enough memory for subspace creation!}");
  658.           return;
  659.       }
  660.  
  661.     if ((answer = NewHandle (16)) == NULL)
  662.       {
  663.         DisposHandle (subspaceHandle);
  664.           returnErrorMsg (paramPtr,
  665.             "{Sorry, out of memory error in XFCN NEWSUBSPACE call!}");
  666.           return;
  667.       }
  668.     ltoaR (*answer, (long)subspaceHandle, 12);
  669.     *(*answer + 12) = '\0';
  670.     paramPtr->returnValue = answer;
  671.  
  672.     return;
  673.     
  674.   }
  675.  
  676.  
  677. /* routine to get rid of a subspace and release that memory:
  678.  *
  679.  *    ("RELEASESUBSPACE", subspaceHandle)
  680.  *    --    returns quietly with nothing if successful in releasing the
  681.  *        subspaceHandle, or noisily with an error message if it fails...
  682.  */
  683.  
  684. void doReleaseSubspace (paramPtr)
  685.   XCmdBlockPtr paramPtr;
  686.   {
  687.     Handle subspaceHandle;
  688.     
  689.     if (paramPtr->paramCount != 2)
  690.       {
  691.           returnErrorMsg (paramPtr,
  692.             "{Sorry, wrong # of params in XFCN RELEASESUBSPACE call!}");
  693.           return;
  694.       }
  695.  
  696.     subspaceHandle = (Handle) atol (*(paramPtr->params[1]));
  697.     if (subspaceHandle == NULL)
  698.       {
  699.           returnErrorMsg (paramPtr,
  700.             "{Sorry, NULL subspaceHandle in XFCN RELEASESUBSPACE call!}");
  701.           return;
  702.       }
  703.     
  704.     DisposHandle (subspaceHandle);
  705.     return;
  706.   }
  707.  
  708.  
  709. /* function to turn on or off bits in a subspace according to their
  710.  * proximity to a given word's occurrences:
  711.  *
  712.  *    ("SETSUBSPACEBITS", wordNum, neighborhoodSize, setOrClear,
  713.  *      keyFileRefNum, ptrFileRefNum, subspaceHandle)
  714.  *    --    returns quietly with nothing if it is successful in setting or
  715.  *        clearing (depending on setOrClear's value, 0 or non-0) the
  716.  *        bits in the subspace flag array in the neighborhood of the
  717.  *        chosen word(s); gives an error msg if there was a problem.
  718.  *        neighborhoodSize is in characters and is used to determine
  719.  *         how many bits to set/clear on each side of the instances...
  720.  */
  721.  
  722. void doSetSubspaceBits (paramPtr)
  723.   XCmdBlockPtr paramPtr;
  724.   {
  725.     long wordNum, neighborhoodSize, bitsToSet, maxTextPtr, instance,
  726.       tp0, tp, tpMax;
  727.     int setOrClear, keyFileRefNum, ptrFileRefNum;
  728.     Handle subspaceHandle;
  729.     KEY_RECORD prevRec, thisRec;
  730.     
  731.     if (paramPtr->paramCount != 7)
  732.       {
  733.           returnErrorMsg (paramPtr,
  734.             "{Sorry, wrong # of parameters in XFCN SETSUBSPACE call!}");
  735.           return;
  736.       }
  737.  
  738.      
  739.     wordNum = atol (*(paramPtr->params[1]));
  740.     neighborhoodSize = atol (*(paramPtr->params[2]));
  741.     setOrClear = atol (*(paramPtr->params[3]));
  742.     keyFileRefNum = atol (*(paramPtr->params[4]));
  743.     ptrFileRefNum = atol (*(paramPtr->params[5]));
  744.     subspaceHandle = (Handle) atol (*(paramPtr->params[6]));
  745.  
  746.     if (wordNum < 0 || neighborhoodSize < 1 ||
  747.             keyFileRefNum == NULL || ptrFileRefNum == NULL ||
  748.             subspaceHandle == NULL)
  749.       {
  750.           returnErrorMsg (paramPtr,
  751.             "{Sorry, bad parameter in XFCN SETSUBSPACE call!}");
  752.           return;
  753.       }
  754.  
  755.     bitsToSet = (neighborhoodSize * 2) / SUBSPACE_QUANTUM + 1;
  756.     maxTextPtr = GetHandleSize (subspaceHandle) * SUBSPACE_QUANTUM * 8;
  757.     getKeyRecord (&prevRec, wordNum - 1, keyFileRefNum);
  758.     getKeyRecord (&thisRec, wordNum, keyFileRefNum);
  759.     
  760.     for (instance = prevRec.ccount; instance < thisRec.ccount; ++instance)
  761.       {
  762.         tp0 = getTextPtr (instance, ptrFileRefNum);
  763.         if (tp0 < 0)
  764.           {
  765.               returnErrorMsg (paramPtr,
  766.                 "{Sorry, getTextPtr I/O error in XFCN SETSUBSPACE call!}");
  767.               return;
  768.           }
  769.         tp = tp0 - (bitsToSet / 2) * SUBSPACE_QUANTUM;
  770.         tpMax = tp + bitsToSet * SUBSPACE_QUANTUM;
  771.         if (tp < 0)
  772.             tp = 0;
  773.         if (tpMax > maxTextPtr)
  774.             tpMax = maxTextPtr;
  775.         for ( ; tp < tpMax; tp += SUBSPACE_QUANTUM)
  776.             setSSBit (tp, setOrClear, subspaceHandle);
  777.       }
  778.     
  779.     return;    
  780.   }
  781.  
  782.  
  783. /* function to grab a chunk of text:
  784.  *
  785.  *    ("TEXT", textPtr, textChunkSize, textOffset, textFileRefNum)
  786.  *    --    returns with (if possible; see below)
  787.  *        textChunkSize bytes of text from the text file,
  788.  *        starting at byte number textPtr-textOffset+1 and ending
  789.  *        just before byte number textPtr-textOffset+textChunkSize+1.
  790.  *        (The '+1' is to match up with HyperCard's 1-based counting
  791.  *        convention, rather than the 0-based C convention!!)
  792.  *        If the file isn't big enough or if textPtr is too near the
  793.  *        beginning or end of the file, cut off the retrieved text
  794.  *        at that boundary and insert the words {beginning of dataspace}
  795.  *        or {end of dataspace}.  ***Do no filtering of the text!***
  796.  *        (Thus, there may be strangenesses if the 'text' file has
  797.  *        '\0' or other nasty characters in it -- sorry about that!)
  798.  *        Restrict textChunkSize to <32000 bytes.  After the text, on
  799.  *        a separate line, return three numbers:  the byte number of
  800.  *        the first char returned relative to the beginning of the text
  801.  *        file, the actual offset within the characters returned
  802.  *        of the originally-requested textPtr, and the byte number
  803.  *        of the character after the last char returned relative to
  804.  *        the beginning of the text file.
  805.  */
  806.  
  807. void doText (paramPtr)
  808.   XCmdBlockPtr paramPtr;
  809.   {
  810.     int textFileRefNum;
  811.     long textPtr, textChunkSize, textOffset, textFileSize, startText,
  812.       endText, count;
  813.     Handle answer;
  814.     char *ansp;
  815.     
  816.     if (paramPtr->paramCount != 5)
  817.       {
  818.           returnErrorMsg (paramPtr,
  819.               "Sorry, wrong number of parameters in XFCN TEXT call!}");
  820.           return;
  821.       }
  822.      
  823.     textPtr = atol (*(paramPtr->params[1]));
  824.     textChunkSize = atol (*(paramPtr->params[2]));
  825.     textOffset = atol (*(paramPtr->params[3]));
  826.     textFileRefNum = atol (*(paramPtr->params[4]));
  827.  
  828.     GetEOF (textFileRefNum, &textFileSize);
  829.  
  830.     if (textPtr < 0 || textPtr > textFileSize || textOffset < 1 ||
  831.          textOffset > textChunkSize || textChunkSize < 1 ||
  832.          textChunkSize > 32000 || textFileRefNum == 0)
  833.       {
  834.           returnErrorMsg (paramPtr,
  835.               "{Sorry, bad parameter in XFCN TEXT call!}");
  836.           return;
  837.       }
  838.     
  839.     startText = textPtr - textOffset + 1;
  840.     if (startText < 0)
  841.         startText = 0;
  842.     endText = textPtr + textChunkSize - textOffset + 1;
  843.     if (endText > textFileSize)
  844.         endText = textFileSize;
  845.     
  846.     count = endText - startText;
  847.     if ((answer = NewHandle (count + 80)) == NULL)
  848.       {
  849.           returnErrorMsg (paramPtr,
  850.               "{Sorry, out of memory error in XFCN TEXT call!}");
  851.           return;
  852.       }
  853.  
  854.     HLock (answer);
  855.     ansp = *answer;
  856.     if (startText == 0)
  857.       {
  858.           strcpy (ansp, "{beginning of dataspace}\r");
  859.           ansp += strlen ("{beginning of dataspace}\r");
  860.           textOffset = textPtr + strlen ("{beginning of dataspace}\r");
  861.       }
  862.  
  863.     if (SetFPos (textFileRefNum, fsFromStart, startText) != noErr ||
  864.             FSRead (textFileRefNum, &count, ansp) != noErr)
  865.       {
  866.           SysBeep (10);
  867.         strcpy (ansp,
  868.             "{Sorry, file I/O error in XFCN TEXT call!}");
  869.         HUnlock (answer);
  870.         paramPtr->returnValue = answer;
  871.         return;
  872.       }
  873.     
  874.     ansp += count;
  875.     *ansp++ = '\r';
  876.     if (endText == textFileSize)
  877.       {
  878.           strcpy (ansp, "{end of dataspace}\r");
  879.           ansp += strlen ("{end of dataspace}\r");
  880.       }
  881.     ltoaR (ansp, startText, 12);
  882.     ansp += 12;
  883.     ltoaR (ansp, textOffset, 12);
  884.     ansp += 12;
  885.     ltoaR (ansp, endText, 12);
  886.     ansp += 12;
  887.     *ansp = '\0';
  888.     HUnlock (answer);
  889.     paramPtr->returnValue = answer;
  890.     return;
  891.   }
  892.  
  893.  
  894.  
  895. /* ------------------------support routines-------------- */
  896.  
  897.  
  898. /* function to set the return value of the XFCN to a chosen error msg;
  899.  * if there isn't enough free memory to give us a Handle to the msg,
  900.  * beep a bunch and then return!
  901.  */
  902.  
  903. void returnErrorMsg (paramPtr, msg)
  904.   XCmdBlockPtr paramPtr;
  905.   char *msg;
  906.   {
  907.     Handle answer;
  908.     int msgLength;
  909.     
  910.     SysBeep (10);
  911.     msgLength = strlen (msg);
  912.     if ((answer = NewHandle (1 + msgLength)) == NULL)
  913.       {
  914.         SysBeep (10);
  915.         SysBeep (10);
  916.         SysBeep (10);
  917.         SysBeep (10);
  918.         SysBeep (10);
  919.         return;
  920.       }
  921.  
  922.     strcpy (*answer, msg);
  923.     paramPtr->returnValue = answer;
  924.     return;
  925.   }
  926.  
  927.  
  928. /* function to fetch an index key record from the key file; if an
  929.  * illegal keyRecNum is asked for, or if any sort of I/O error is
  930.  * reported by SetFPos() or FSRead(), return 0 ccount and blank kkey....
  931.  */
  932.  
  933. void getKeyRecord (keyRecp, keyRecNum, keyFileRefNum)
  934.   KEY_RECORD *keyRecp;
  935.   long keyRecNum;
  936.   int keyFileRefNum;
  937.   {
  938.     long count;
  939.     register int i;
  940.  
  941.     count = sizeof(KEY_RECORD);
  942.             
  943.     if (keyRecNum < 0 ||
  944.             SetFPos (keyFileRefNum, fsFromStart, 
  945.                     keyRecNum * sizeof(KEY_RECORD)) != noErr ||
  946.             FSRead (keyFileRefNum, &count, keyRecp) != noErr)
  947.       {
  948.         for (i = 0; i < KEY_LENGTH; ++i)
  949.             keyRecp->kkey[i] = ' ';
  950.           keyRecp->ccount = 0;
  951.       }
  952.  
  953.     return;
  954.   }
  955.  
  956. /* function to fetch the value of the nth ptr from file ptrFileRefNum;
  957.  * return illegal value (-1) for result if something goes wrong....
  958.  */
  959.  
  960. long getTextPtr (n, ptrFileRefNum)
  961.   long n;
  962.   int ptrFileRefNum;
  963.   {
  964.     long bytes = sizeof(long), result;
  965.     
  966.     if (SetFPos (ptrFileRefNum, fsFromStart, n * sizeof(long)) != noErr ||
  967.             FSRead (ptrFileRefNum, &bytes, &result) != noErr)
  968.         return (-1);
  969.  
  970.     return (result);
  971.   }
  972.  
  973.  
  974. /* function to fetch a filtered line of text from the file ... fill
  975.  * in with blanks if try to fetch from before the beginning of the
  976.  * file or after the end of the file ... filter all control characters
  977.  * by turning them into spaces ....
  978.  */
  979.  
  980. void getContextLine (bytes, start, refNum, ans)
  981.   int refNum;
  982.   long bytes, start;
  983.   char *ans;
  984.   {
  985.     register int i = 0;
  986.     long origbytes;
  987.     
  988.     origbytes = bytes;
  989.     if (start < 0)
  990.         for (i = 0; i < -start; ++i)
  991.             ans[i] = ' ';
  992.     bytes -= i;
  993.     
  994.     if (SetFPos (refNum, fsFromStart, start + i) != noErr ||
  995.             FSRead (refNum, &bytes, ans + i) != noErr)
  996.       {
  997.         if (origbytes >
  998.                 strlen ("{Sorry, file I/O error in XFCN CONTEXT call!}"))
  999.           {
  1000.             strcpy (ans, "{Sorry, file I/O error in XFCN CONTEXT call!}");
  1001.             bytes = origbytes;
  1002.           }
  1003.         else
  1004.             SysBeep (10);
  1005.       }
  1006.     
  1007.     if (bytes + i < origbytes)
  1008.         for (i += bytes; i < origbytes; ++i)
  1009.             ans[i] = ' ';
  1010.     for (i = 0; i < origbytes; ++i)
  1011.           if (ans[i] < 32 || ans[i] == 127)
  1012.               ans[i] = ' ';
  1013.         
  1014.     return;
  1015.   }
  1016.  
  1017.  
  1018. /* function to format an index record, with the count right-justified
  1019.  * followed by a space, then the key word itself, followed by a '\r'.
  1020.  */
  1021.  
  1022. void buildIndexAnswer (ansp, cwidth, kwidth, count, key)
  1023.   char *ansp, *key;
  1024.   long count;
  1025.   int cwidth, kwidth;
  1026.   {
  1027.     register int i;
  1028.  
  1029.     ltoaR (ansp, count, cwidth);
  1030.     ansp += cwidth;
  1031.     *ansp++ = ' ';
  1032.     for (i = 0; i < kwidth; ++i)
  1033.         *ansp++ = *key++;
  1034.     *ansp = '\r';
  1035.     
  1036.     return;
  1037.   }
  1038.  
  1039.  
  1040. /* function to format an index record when working in a subspace; like
  1041.  * buildIndexAnswer function above, but with information about how many
  1042.  * instances of each word are in the working subspace.  Specifically,
  1043.  * give a percentage estimate based on the last maxIndexSampleCount
  1044.  * instances for a word that occurs more than maxIndexSampleCount times
  1045.  * (e.g., " ~37% "), and for less frequently occurring words give the
  1046.  * actual fraction of valid/total instances (e.g., " 17/49 ").
  1047.  */
  1048.  
  1049. void buildSubIndexAnswer (ansp, cwidth, kwidth, maxSample,
  1050.             prevCcount, thisCcount, key, subspaceHandle, ptrFileRefNum)
  1051.   char *ansp, *key;
  1052.   int cwidth, kwidth, ptrFileRefNum;
  1053.   long maxSample, prevCcount, thisCcount;
  1054.   Handle subspaceHandle;
  1055.   {
  1056.       long startCcount, instance, goodInstances;
  1057.       register int i;
  1058.       int goodPercent, subWidth;
  1059.       
  1060.     if (thisCcount - prevCcount > maxSample)
  1061.         startCcount = thisCcount - maxSample;
  1062.     else
  1063.         startCcount = prevCcount;
  1064.     
  1065.     goodInstances = 0;
  1066.     for (instance = startCcount; instance < thisCcount; ++instance)
  1067.         if (inSubspace (getTextPtr (instance, ptrFileRefNum),
  1068.                             subspaceHandle))
  1069.             ++goodInstances;
  1070.     
  1071.     if (thisCcount - prevCcount > maxSample)
  1072.       {
  1073.         goodPercent = (100 * goodInstances) / (thisCcount - startCcount);
  1074.         *ansp++ = '~';
  1075.         ltoaR (ansp, goodPercent, 3);
  1076.         ansp += 3;
  1077.         *ansp++ = '%';
  1078.         for (i = 5; i < cwidth; ++i)
  1079.             *ansp++ = ' ';
  1080.       }
  1081.     else
  1082.       {
  1083.         subWidth = (cwidth - 1) / 2;
  1084.         ltoaR (ansp, goodInstances, subWidth);
  1085.         ansp += subWidth;
  1086.         *ansp++ = '/';
  1087.         ltoaR (ansp, thisCcount - prevCcount, subWidth);
  1088.         ansp += subWidth;
  1089.         for (i = 2 * subWidth + 1; i < cwidth; ++i)
  1090.             *ansp++ = ' ';
  1091.       }
  1092.     
  1093.     *ansp++ = ' ';
  1094.     for (i = 0; i < kwidth; ++i)
  1095.         *ansp++ = *key++;
  1096.     *ansp = '\r';
  1097.  
  1098.     return;
  1099.   }
  1100.  
  1101.  
  1102. /* function to determine if a given textPtr is in the subspace of
  1103.  * interest ... do it by a simple computation and look-up in the
  1104.  * bit array of subspace flags...
  1105.  */
  1106.  
  1107. int inSubspace (textPtr, subspaceHandle)
  1108.   long textPtr;
  1109.   Handle subspaceHandle;
  1110.   {
  1111.     int bitNum;
  1112.     long byteNum;
  1113.  
  1114.     bitNum = (textPtr % (8 * SUBSPACE_QUANTUM)) / SUBSPACE_QUANTUM;
  1115.     byteNum = textPtr / (8 * SUBSPACE_QUANTUM);
  1116.  
  1117.     return (((*subspaceHandle)[byteNum] >> bitNum) & 1);
  1118.   }
  1119.  
  1120.  
  1121. /* function to set or clear a bit in the subspace array...
  1122.  */
  1123.  
  1124. void setSSBit (textPtr, setOrClear, subspaceHandle)
  1125.   long textPtr;
  1126.   int setOrClear;
  1127.   Handle subspaceHandle;
  1128.   {
  1129.     int bitNum;
  1130.     long byteNum;
  1131.  
  1132.     bitNum = (textPtr % (8 * SUBSPACE_QUANTUM)) / SUBSPACE_QUANTUM;
  1133.     byteNum = textPtr / (8 * SUBSPACE_QUANTUM);
  1134.  
  1135.     if (setOrClear)
  1136.         *(*subspaceHandle + byteNum) |= 1 << bitNum;
  1137.     else
  1138.         *(*subspaceHandle + byteNum) &= ~(1 << bitNum);
  1139.     
  1140.     return;
  1141.   }
  1142.  
  1143.  
  1144. /* function to copy a string from one place to another, in a rather
  1145.  * obvious fashion ... adapted from the LSC library, and K&R p.101 ....
  1146.  */
  1147.  
  1148. char *strcpy (s1, s2)
  1149.   register char *s1, *s2;
  1150.   {
  1151.     char *s = s1;
  1152.     
  1153.     while (*s1++ = *s2++)
  1154.         ;
  1155.     return (s);
  1156.   }
  1157.  
  1158.  
  1159. /* function to determine the length of a string ... standard thing,
  1160.  * adapted from the LSC library, and K&R p.98 ....
  1161.  */
  1162.  
  1163. int strlen (s)
  1164.   register char *s;
  1165.   {
  1166.     char *s0 = s;
  1167.  
  1168.     while (*s++)
  1169.         ;
  1170.     return (s - s0 - 1);
  1171.   }
  1172.  
  1173. /* my function to compare two strings and give a result as to who is
  1174.  * alphabetically earlier.  Note that this is almost the same as strncmp()
  1175.  * with the fixed value of KEY_LENGTH as the maximum comparison distance,
  1176.  * except that I must be sure to handle the non-ASCII funny letters in
  1177.  * the Apple character set properly/consistently ... hence the need to
  1178.  * declare s1 and s2 to be type unsigned char *...
  1179.  */
  1180.  
  1181. int zstrcmp (s1, s2)
  1182.   register unsigned char *s1, *s2;
  1183.   {
  1184.     register int n = KEY_LENGTH;
  1185.     
  1186.     for (; --n && *s1 == *s2; s1++, s2++)
  1187.         if (!*s1)
  1188.             break;
  1189.         
  1190.     return (*s1 - *s2);
  1191.   }
  1192.  
  1193.  
  1194. /* function to convert alphanumeric string to a long int, from K&R & LSC
  1195.  * library.... simplified to avoid using isspace() & isdigit() ....
  1196.  */
  1197.  
  1198. long atol (s)
  1199.   register char *s;
  1200.   {
  1201.     int signflag = 0;
  1202.     register long r = 0;
  1203.  
  1204.     while (*s == ' ')
  1205.         s++;
  1206.         
  1207.     if (*s == '-')
  1208.       {
  1209.         signflag = 1;
  1210.         s++;
  1211.       }
  1212.     else if (*s == '+')
  1213.          s++;
  1214.  
  1215.     while (*s >= '0' && *s <= '9') 
  1216.         r = r * 10 + (*s++ - '0');
  1217.     
  1218.     return (signflag ? -r : r);
  1219. }
  1220.  
  1221.  
  1222. /* function to convert a number into a string of width maxDigits and
  1223.  * store it right-justified, blank-filled on left; based on K&R p. 60
  1224.  * example of itoa().
  1225.  *
  1226.  * Error handling:  put a '>' or '<' in leading place to warn of an
  1227.  * overflow (no room for digits on a positive or negative number,
  1228.  * respectively), and put a '^' in leading place to warn if no room
  1229.  * for '-' sign on negative number...
  1230.  */
  1231.  
  1232. void ltoaR (ansp, n, maxDigits)
  1233.   register char *ansp;
  1234.   register long n;
  1235.   int maxDigits;
  1236.   {
  1237.     register int i;
  1238.     long sign;
  1239.     
  1240.     i = maxDigits - 1;
  1241.     if ((sign = n) < 0)
  1242.         n = -n;
  1243.     
  1244.     do
  1245.       {
  1246.           ansp[i--] = n % 10 + '0';
  1247.       }
  1248.     while ((n /= 10) > 0 && i >= 0);
  1249.     
  1250.     if (i < 0 && n > 0)        /* ran out of room with digits still to go */
  1251.       {
  1252.         if (sign > 0)
  1253.             ansp[0] = '>';            /* positive overflow signal */
  1254.         else
  1255.             ansp[0] = '<';            /* negative overflow signal */
  1256.       }
  1257.     else
  1258.       {
  1259.         if (sign < 0)
  1260.             if (i >= 0)
  1261.                 ansp[i--] = '-';
  1262.             else
  1263.                 ansp[0] = '^';        /* no room for '-' sign signal */
  1264.         for ( ; i >= 0; --i)
  1265.             ansp[i] = ' ';
  1266.       }
  1267.  
  1268.     return;
  1269.   }
  1270.   
  1271.  
  1272.  
  1273.  
  1274. /* -----------------command syntax & calling conventions--------------- */
  1275.  
  1276. /*
  1277.  *    ("CONTEXT", instanceNum, contextLines, targetContextLine,
  1278.  *      contextLineLength, contextWordOffset, maxContextLinesSkipped,
  1279.  *      ptrFileRefNum, textFileRefNum, subspaceHandle)
  1280.  *    --    returns with contextLines of display followed by contextLines
  1281.  *        of instanceNum-textPtr pairs, with context instance instanceNum
  1282.  *        on line targetContextLine...
  1283.  *
  1284.  *    ("EMPTYSUBSPACE", subspaceHandle)
  1285.  *    --    returns quietly with nothing if it successfully sets all bits
  1286.  *        in the subspace flag array to zero; beeps and gives an error msg
  1287.  *        if it fails somehow...
  1288.  *
  1289.  *    ("FILLSUBSPACE", subspaceHandle)
  1290.  *    --    returns quietly with nothing if it successfully sets all bits
  1291.  *        in the subspace flag array to one; beeps and gives an error msg
  1292.  *        if failure...
  1293.  *
  1294.  *    ("INDEX", wordNum, indexLines, maxIndexSampleCount, indexCountWidth,
  1295.  *      indexKeyWidth, keyFileRefNum, ptrFileRefNum, subspaceHandle)
  1296.  *    --    returns with indexLines of index window display, followed by
  1297.  *        indexLines of instanceNums.  The index lines are:
  1298.  *        indexCountWidth columns of occurrence count info (right-justified),
  1299.  *        a blank column, and indexKeyWidth columns of keyWord (in all
  1300.  *        caps, left-justified).  Demand that indexCountWidth be at least
  1301.  *        5, to allow for subindex count display, and that indexKeyWidth
  1302.  *        be in the range 1 through KEY_LENGTH = 28 ...
  1303.  *
  1304.  *    ("LOCATE", targetString, keyFileRefNum)
  1305.  *    --    returns wordNum for the targetString if it is found in the
  1306.  *        key file; otherwise returns wordNum for the word alphabetically
  1307.  *        preceding targetString followed by "{targetString not found!}"
  1308.  *        on the second line of the answer...
  1309.  *
  1310.  *    ("NEWSUBSPACE", textFileRefNum)
  1311.  *    --    returns subspaceHandle for a new subspace that it creates, big
  1312.  *        enough to do subspace browsing -- but does NOT initialize that
  1313.  *        subspace or check to see whether another subspace already
  1314.  *        exists.  Beeps and gives error msg if it fails...
  1315.  *
  1316.  *    ("RELEASESUBSPACE", subspaceHandle)
  1317.  *    --    returns quietly with nothing if successful in releasing the
  1318.  *        subspaceHandle, or noisily with an error message if it fails...
  1319.  *
  1320.  *    ("SETSUBSPACEBITS", wordNum, neighborhoodSize, setOrClear,
  1321.  *      keyFileRefNum, ptrFileRefNum, subspaceHandle)
  1322.  *    --    returns quietly with nothing if it is successful in setting or
  1323.  *        clearing (depending on setOrClear's value, 0 or non-0) the
  1324.  *        bits in the subspace flag array in the neighborhood of the
  1325.  *        chosen word(s); gives an error msg if there was a problem.
  1326.  *        neighborhoodSize is in characters and is used to determine
  1327.  *         how many bits to set/clear on each side of the instances...
  1328.  *
  1329.  *    ("TEXT", textPtr, textChunkSize, textOffset, textFileRefNum)
  1330.  *    --    returns with (if possible; see below)
  1331.  *        textChunkSize bytes of text from the text file,
  1332.  *        starting at byte number textPtr-textOffset+1 and ending
  1333.  *        just before byte number textPtr-textOffset+textChunkSize+1.
  1334.  *        (The '+1' is to match up with HyperCard's 1-based counting
  1335.  *        convention, rather than the 0-based C convention!!)
  1336.  *        If the file isn't big enough or if textPtr is too near the
  1337.  *        beginning or end of the file, cut off the retrieved text
  1338.  *        at that boundary and insert the words {beginning of dataspace}
  1339.  *        or {end of dataspace}.  ***Do no filtering of the text!***
  1340.  *        (Thus, there may be strangenesses if the 'text' file has
  1341.  *        '\0' or other nasty characters in it -- sorry about that!)
  1342.  *        Restrict textChunkSize to <32000 bytes.  After the text, on
  1343.  *        a separate line, return three numbers:  the byte number of
  1344.  *        the first char returned relative to the beginning of the text
  1345.  *        file, the actual offset within the characters returned
  1346.  *        of the originally-requested textPtr, and the byte number
  1347.  *        of the character after the last char returned relative to
  1348.  *        the beginning of the text file.
  1349.  */
  1350.  
  1351.